/**
 * Author: Jackin (qin.zhang@uni.lu)
 * Date: August, 2013
 * (c) 2013 University of Luxembourg - Interdisciplinary Centre for Security Reliability and Trust (SnT)
 * All rights reserved
 * GPLv3 to be found at: http://www.gnu.org/licenses/quick-guide-gplv3.html
 */

package lu.uni.geko.ext.ram;

import lu.uni.geko.common.GeKoConstants;
import lu.uni.geko.resources.MainResourceLoader;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;

/**
 * This is a utility class that offers static methods for additional operations on RAM aspects which
 * are necessary for model weaving process, e.g. instantiating the dependent aspects before weaving,
 * etc.
 * 
 * @author Jackin
 */
public final class RamUtil
{

	/**
	 * Create temporary URIs for future decomposed compartments of the RAM aspects, e.g.: temp.ram_av,
	 * temp.ram_pc and temp.pc2avmapping.
	 * 
	 * @param uri
	 *          The root base RAM aspect URI
	 * @param newFileExt
	 *          The new file extension for the temporary URI, choosing from
	 *          {@link lu.uni.geko.ext.ram.RamConstants lu.uni.geko.ext.ram.RamConstants}.
	 * @return The temporary URI created, e.g. temp.pc2avmapping, etc.
	 */
	public static final URI createTempURI(final URI uri, final String newFileExt)
	{
		String fileExt = uri.fileExtension();
		if (fileExt != null)
		{
			URI uriWithoutFileExt = uri.trimFileExtension();
			return uriWithoutFileExt.trimSegments(1).appendSegment("temp").appendFileExtension(newFileExt);
		} else
		{
			throw new RuntimeException("The URI '" + uri + "' has no file extension!");
		}
	}
	
	/**
	 * This method is used to store a backup copy of an aspect (the original URI+"_bak"), which keeps
	 * its original model .<br/>
	 * <br/>
	 * 
	 * @param uri
	 *          The URI of the aspect which is going to be backed up.
	 * @return The URI of the backed-up aspect with a suffix "_bak" on its original URI.
	 */
	public static final URI backup(final URI uri)
	{
		String fileExt = uri.fileExtension();
		if (fileExt != null)
		{
			URI backupURI = uri.trimFileExtension();
			String resultFileName = backupURI.lastSegment() + "_bak";
			backupURI = backupURI.trimSegments(1).appendSegment(resultFileName).appendFileExtension(fileExt);

			EObject baseRootElement = MainResourceLoader.getUniqueContentRoot(uri, "backup model");
			EObject backupRootElement = EcoreUtil.copy(baseRootElement);
			MainResourceLoader.saveEObjectAsOnlyContent(backupRootElement, backupURI);
			
			return backupURI;
			
		} else
		{
			throw new RuntimeException("The URI '" + uri + "' has no file extension!");
		}
	}

	/**
	 * This method is used to instantiate the dependent aspects, which means changing the instantiated
	 * element names to the corresponding ones in the depending aspect.
	 * 
	 * @param instantiation
	 *          The instantiation (EList) from the depending aspect
	 * @throws Exception
	 *           RAM-specific exception regarding instantiation
	 */
	public static final void instantiate(ram.Instantiation instantiation) throws Exception
	{
		for (ram.ClassifierMapping classifierMapping : instantiation.getMappings())
		{

			instantiateMapping(classifierMapping);

			// instantiate attribute mapping if applicable
			if (classifierMapping.getAttributeMappings() != null)
				for (ram.AttributeMapping attributeMapping : classifierMapping.getAttributeMappings())
					instantiateMapping(attributeMapping);

			// instantiate operation mapping if applicable
			if (classifierMapping.getOperationMappings() != null)
				for (ram.OperationMapping operationMapping : classifierMapping.getOperationMappings())
				{
					instantiateMapping(operationMapping);

					// instantiate parameter mapping if applicable
					if (operationMapping.getParameterMappings() != null)
						for (ram.ParameterMapping parameterMapping : operationMapping.getParameterMappings())
							instantiateMapping(parameterMapping);
				}
		}
	}

	/**
	 * This method is a general instantiating function called only by the public method "instantiate"
	 * in this utility class.
	 * 
	 * @param mapping
	 *          Super instantiation mapping
	 * @throws Exception
	 *           RAM-specific exception regarding instantiation
	 */
	private static void instantiateMapping(ram.Mapping mapping) throws Exception
	{
		ram.MappableElement fromElement = mapping.getFromElement();
		ram.MappableElement toElement = mapping.getToElement();

		/*
		 * check if the mapping is complete and semantically equal (means they are the same kind of
		 * elements)
		 */
		checkIntegrity(fromElement, toElement);

		/*
		 * Note the order! According to RAM desirable way, the to-element is the one in the depending
		 * aspect, while the from-element is that in the dependent aspect.
		 */
		fromElement.setName(toElement.getName());
	}

	/**
	 * This method is called only by the private method "instantiateMapping" in this utility class. It
	 * is used to check the integrity of each instantiation mapping.
	 * 
	 * @param e1
	 *          First element from a mapping entry
	 * @param e2
	 *          Second element from a mapping entry
	 * @throws Exception
	 *           RAM-specific exception regarding instantiation mapping entry integrity
	 */
	private static void checkIntegrity(ram.MappableElement e1, ram.MappableElement e2) throws Exception
	{
		/*
		 * Check the integrity of the two mappable elements. 1. Two elements in the mapping must exist
		 * (completeness). No "null" entry. 2. Two elements must be semantically equal, means sharing
		 * the same meta-class.
		 */
		// check completeness
		if (e1 == null || e2 == null)
			throw new Exception("The instantiation is not complete! " + "The from-element: " + e1 + "; the to-element: " + e2);

		// check semantically equal
		if (e1.eClass() != e2.eClass())
			throw new Exception("The instantiation contains two elements which are not semantically equal! " + "The from-element: " + e1
					+ "; the to-element: " + e2);

		/*
		 * No-use according to the RAM specification
		 * 
		 * // check if containing proxy if (e1.eIsProxy() || e2.eIsProxy()) throw new
		 * Exception("The instantiation contains Proxy! " + "The from-element: " + e1 +
		 * "; the to-element: " + e2);
		 */
	}

	/**
	 * Make an advice copy of the base element by appending advice package suffix to the base package
	 * name
	 * 
	 * @param eObject
	 *          The base element
	 * @return An Advice copy of the base element
	 */
	public static EObject copyAdvice(final EObject eObject)
	{
		RamCopier copier = new RamCopier(GeKoConstants.getAvMMPkgNsURIAppendage());
		return copier.copy(eObject);
	}

	/**
	 * Make a pointcut copy of the base element by appending pointcut package suffix to the base
	 * package name
	 * 
	 * @param eObject
	 *          The base element
	 * @return A pointcut copy of the base element
	 */
	public static EObject copyPointcut(final EObject eObject)
	{
		RamCopier copier = new RamCopier(GeKoConstants.getPcMMFilenameAppendage());
		return copier.copy(eObject);
	}

}
